﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Reflection;

namespace CAN_Monitor
{
    /// <summary>
    /// Defines the operator for comparing values and statements
    /// </summary>
    enum FilterRuleOperator
    {
        EQUALS,
        NOT_EQUALS,
        GREATER,
        LESS,
        GREATER_EQUAL,
        LESS_EQUAL,
        NOT,
        AND,
        OR
    }

    //----------------------------------------------------------------------------------------

    struct FilterValue
    {
        #region ctor


        public FilterValue(object val)
        {
            if (val is string)
            {
                string s = val as string;
                int iTemp;
                double dTemp;

                if (int.TryParse(s, out iTemp))
                {
                    mValue = iTemp;
                }
                else if (double.TryParse(s, out dTemp))
                {
                    mValue = dTemp;
                }
                else
                {
                    mValue = val;
                }

            }
            else
            {
                mValue = val;
            }
        }

        #endregion

        #region Members

        private object mValue;

        #endregion

        #region Operators

        public static bool operator ==(FilterValue val1, FilterValue val2)
        {
            //Check the std types
            if (val1.mValue is int)
            {
                #region val1IsInt

                if (val2.mValue is int)
                {
                    return ((int)val1.mValue == (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((int)val1.mValue == (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((int)val1.mValue == (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((int)val1.mValue == (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((int)val1.mValue == (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((int)val1.mValue == (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((int)val1.mValue == (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is double)
            {
                #region val1IsDouble

                if (val2.mValue is int)
                {
                    return ((double)val1.mValue == (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((double)val1.mValue == (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((double)val1.mValue == (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((double)val1.mValue == (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((double)val1.mValue == (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((double)val1.mValue == (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((double)val1.mValue == (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is float)
            {
                #region val1IsFloat

                if (val2.mValue is int)
                {
                    return ((float)val1.mValue == (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((float)val1.mValue == (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((float)val1.mValue == (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((float)val1.mValue == (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((float)val1.mValue == (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((ushort)val1.mValue == (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((float)val1.mValue == (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is uint)
            {
                #region val1IsUInt

                if (val2.mValue is int)
                {
                    return ((uint)val1.mValue == (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((uint)val1.mValue == (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((uint)val1.mValue == (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((uint)val1.mValue == (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((uint)val1.mValue == (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((uint)val1.mValue == (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((uint)val1.mValue == (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is short)
            {
                #region val1IsShort

                if (val2.mValue is int)
                {
                    return ((short)val1.mValue == (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((short)val1.mValue == (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((short)val1.mValue == (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((short)val1.mValue == (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((short)val1.mValue == (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((short)val1.mValue == (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((short)val1.mValue == (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is ushort)
            {
                #region val1IsUShort

                if (val2.mValue is int)
                {
                    return ((ushort)val1.mValue == (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((ushort)val1.mValue == (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((ushort)val1.mValue == (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((ushort)val1.mValue == (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((ushort)val1.mValue == (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((ushort)val1.mValue == (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((ushort)val1.mValue == (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is byte)
            {
                #region val1IsByte

                if (val2.mValue is int)
                {
                    return ((byte)val1.mValue == (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((byte)val1.mValue == (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((byte)val1.mValue == (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((byte)val1.mValue == (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((byte)val1.mValue == (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((ushort)val1.mValue == (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((byte)val1.mValue == (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is string && val2.mValue is string)
            {
                return (val1.mValue as string) == (val2.mValue as string);
            }
            else
            {
                return false;
            }
        }

        //----------------------------------------------------------------------------------------

        public static bool operator !=(FilterValue val1, FilterValue val2)
        {
            return !(val1 == val2);
        }

        //----------------------------------------------------------------------------------------

        public static bool operator <(FilterValue val1, FilterValue val2)
        {
            //Check the std types
            if (val1.mValue is int)
            {
                #region val1IsInt

                if (val2.mValue is int)
                {
                    return ((int)val1.mValue < (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((int)val1.mValue < (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((int)val1.mValue < (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((int)val1.mValue < (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((int)val1.mValue < (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((int)val1.mValue < (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((int)val1.mValue < (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is double)
            {
                #region val1IsDouble

                if (val2.mValue is int)
                {
                    return ((double)val1.mValue < (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((double)val1.mValue < (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((double)val1.mValue < (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((double)val1.mValue < (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((double)val1.mValue < (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((double)val1.mValue < (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((double)val1.mValue < (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is float)
            {
                #region val1IsFloat

                if (val2.mValue is int)
                {
                    return ((float)val1.mValue < (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((float)val1.mValue < (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((float)val1.mValue < (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((float)val1.mValue < (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((float)val1.mValue < (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((ushort)val1.mValue < (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((float)val1.mValue < (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is uint)
            {
                #region val1IsUInt

                if (val2.mValue is int)
                {
                    return ((uint)val1.mValue < (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((uint)val1.mValue < (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((uint)val1.mValue < (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((uint)val1.mValue < (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((uint)val1.mValue < (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((uint)val1.mValue < (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((uint)val1.mValue < (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is short)
            {
                #region val1IsShort

                if (val2.mValue is int)
                {
                    return ((short)val1.mValue < (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((short)val1.mValue < (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((short)val1.mValue < (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((short)val1.mValue < (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((short)val1.mValue < (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((short)val1.mValue < (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((short)val1.mValue < (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is ushort)
            {
                #region val1IsUShort

                if (val2.mValue is int)
                {
                    return ((ushort)val1.mValue < (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((ushort)val1.mValue < (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((ushort)val1.mValue < (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((ushort)val1.mValue < (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((ushort)val1.mValue < (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((ushort)val1.mValue < (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((ushort)val1.mValue < (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is byte)
            {
                #region val1IsByte

                if (val2.mValue is int)
                {
                    return ((byte)val1.mValue < (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((byte)val1.mValue < (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((byte)val1.mValue < (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((byte)val1.mValue < (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((byte)val1.mValue < (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((ushort)val1.mValue < (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((byte)val1.mValue < (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else
            {
                return false;
            }
        }

        //----------------------------------------------------------------------------------------

        public static bool operator <=(FilterValue val1, FilterValue val2)
        {
            if (val1 == val2 || val1 < val2)
                return true;
            else
                return false;
        }
        //----------------------------------------------------------------------------------------

        public static bool operator >(FilterValue val1, FilterValue val2)
        {
            //Check the std types
            if (val1.mValue is int)
            {
                #region val1IsInt

                if (val2.mValue is int)
                {
                    return ((int)val1.mValue > (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((int)val1.mValue > (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((int)val1.mValue > (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((int)val1.mValue > (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((int)val1.mValue > (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((int)val1.mValue > (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((int)val1.mValue > (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is double)
            {
                #region val1IsDouble

                if (val2.mValue is int)
                {
                    return ((double)val1.mValue > (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((double)val1.mValue > (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((double)val1.mValue > (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((double)val1.mValue > (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((double)val1.mValue > (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((double)val1.mValue > (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((double)val1.mValue > (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is float)
            {
                #region val1IsFloat

                if (val2.mValue is int)
                {
                    return ((float)val1.mValue > (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((float)val1.mValue > (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((float)val1.mValue > (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((float)val1.mValue > (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((float)val1.mValue > (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((ushort)val1.mValue > (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((float)val1.mValue > (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is uint)
            {
                #region val1IsUInt

                if (val2.mValue is int)
                {
                    return ((uint)val1.mValue > (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((uint)val1.mValue > (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((uint)val1.mValue > (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((uint)val1.mValue > (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((uint)val1.mValue > (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((uint)val1.mValue > (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((uint)val1.mValue > (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is short)
            {
                #region val1IsShort

                if (val2.mValue is int)
                {
                    return ((short)val1.mValue > (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((short)val1.mValue > (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((short)val1.mValue > (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((short)val1.mValue > (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((short)val1.mValue > (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((short)val1.mValue > (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((short)val1.mValue > (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is ushort)
            {
                #region val1IsUShort

                if (val2.mValue is int)
                {
                    return ((ushort)val1.mValue > (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((ushort)val1.mValue > (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((ushort)val1.mValue > (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((ushort)val1.mValue > (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((ushort)val1.mValue > (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((ushort)val1.mValue > (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((ushort)val1.mValue > (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else if (val1.mValue is byte)
            {
                #region val1IsByte

                if (val2.mValue is int)
                {
                    return ((byte)val1.mValue > (int)val2.mValue);
                }
                else if (val2.mValue is byte)
                {
                    return ((byte)val1.mValue > (byte)val2.mValue);
                }
                else if (val2.mValue is float)
                {
                    return ((byte)val1.mValue > (float)val2.mValue);
                }
                else if (val2.mValue is double)
                {
                    return ((byte)val1.mValue > (double)val2.mValue);
                }
                else if (val2.mValue is short)
                {
                    return ((byte)val1.mValue > (short)val2.mValue);
                }
                else if (val2.mValue is ushort)
                {
                    return ((ushort)val1.mValue > (ushort)val2.mValue);
                }
                else if (val2.mValue is uint)
                {
                    return ((byte)val1.mValue > (uint)val2.mValue);
                }
                else
                    return false;

                #endregion
            }
            else
            {
                return false;
            }
        }

        //----------------------------------------------------------------------------------------

        public static bool operator >=(FilterValue val1, FilterValue val2)
        {
            if (val1 == val2 || val1 > val2)
                return true;
            else
                return false;
        }

        //----------------------------------------------------------------------------------------

        #endregion
    }

    //----------------------------------------------------------------------------------------

    class FilterRule
    {
        #region ctor

        /// <summary>
        /// Constructor for the filterrule
        /// </summary>
        /// <param name="value1">First value for comparing</param>
        /// <param name="op">Operator for comparsion</param>
        /// <param name="value2">Second value for comparing</param>
        public FilterRule(string valueName, FilterRuleOperator op, FilterValue value)
        {
            this.mValueName = valueName;
            this.mValue = value;
            this.mOperator = op;
        }

        /// <summary>
        /// Contructor for connecting two rules with an operator
        /// </summary>
        /// <param name="rule1">first rule</param>
        /// <param name="op">operator that should be used (only AND,OR are valid)</param>
        /// <param name="rule2">second rule</param>
        public FilterRule(FilterRule rule1, FilterRuleOperator op, FilterRule rule2)
        {
            if (op == FilterRuleOperator.AND || op == FilterRuleOperator.OR)
            {
                this.mRule1 = rule1;
                this.mRule2 = rule2;
                this.mOperator = op;
            }
            else
                throw new Exception("Syntax error!\nTwo rules can only be connected by AND,OR!");
        }

        #endregion

        #region Members

        //Normal vars
        private String mValueName;
        private FilterValue mValue;
        private FilterRuleOperator mOperator;

        //Vars for compounded rules
        private FilterRule mRule1 = null;
        private FilterRule mRule2 = null;

        #endregion

        #region Properties

        #endregion

        #region Methods

        /// <summary>
        /// Checks if the packet matches the rule
        /// </summary>
        /// <param name="packet">The packet used for checking</param>
        /// <returns>True if the packet matches the rule otherwise false</returns>
        public bool Check(CANLib.CANPacket packet)
        {
            bool matches = false;

            Type canPacketType = typeof(CANLib.CANPacket);
            PropertyInfo[] infos = canPacketType.GetProperties();
            PropertyInfo checkProperty = null;

            if (this.mOperator == FilterRuleOperator.AND)
            {
                matches = (mRule1.Check(packet) && mRule2.Check(packet));
            }
            else if (this.mOperator == FilterRuleOperator.OR)
            {
                matches = (mRule1.Check(packet) || mRule2.Check(packet));
            }
            else
            {

                //Search properties for richt property name
                foreach (PropertyInfo info in infos)
                {
                    if (info.Name == this.mValueName)
                    {
                        checkProperty = info;
                        break;
                    }
                }

                if (checkProperty != null)
                {
                    //Check for match of rule
                    switch (this.mOperator)
                    {
                        case FilterRuleOperator.EQUALS:
                            matches = (new FilterValue(checkProperty.GetValue(packet, null)) == mValue);
                            break;
                        case FilterRuleOperator.NOT_EQUALS:
                            matches = (new FilterValue(checkProperty.GetValue(packet, null)) != mValue);
                            break;
                        case FilterRuleOperator.GREATER:
                            matches = (new FilterValue(checkProperty.GetValue(packet, null)) > mValue);
                            break;
                        case FilterRuleOperator.GREATER_EQUAL:
                            matches = (new FilterValue(checkProperty.GetValue(packet, null)) >= mValue);
                            break;
                        case FilterRuleOperator.LESS:
                            matches = (new FilterValue(checkProperty.GetValue(packet, null)) < mValue);
                            break;
                        case FilterRuleOperator.LESS_EQUAL:
                            matches = (new FilterValue(checkProperty.GetValue(packet, null)) <= mValue);
                            break;
                    }
                }//if
            }//else

            return matches;
        }

        #endregion
    }
}
